<?php
declare (strict_types=1);

namespace app\tools;

use app\exception\LogicException;
use Carbon\Carbon;
use Exception;
use WeChatPay\Builder;
use WeChatPay\BuilderChainable;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Util\PemUtil;
use WeChatPay\Crypto\AesGcm;
use WeChatPay\Formatter;

final class WechatpayTool
{
    /**
     * @return BuilderChainable
     */
    public function getInstance()
    {
        // 从本地文件中加载「商户API私钥」，「商户API私钥」会用来生成请求的签名
        // $merchantPrivateKeyFilePath = 'file:///path/to/merchant/apiclient_key.pem';
        $merchantPrivateKeyFilePath = 'file://' . app_path('cert' . DIRECTORY_SEPARATOR . 'wx') . 'apiclient_key.pem';
        // dd($merchantPrivateKeyFilePath);
        $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);

        // 「商户API证书」的「证书序列号」
        $merchantCertificateSerial = '3DFAE1E08F8D461777A1212215DB4A91A69D2CAF';

        // 从本地文件中加载「微信支付平台证书」，用来验证微信支付应答的签名
        $platformCertificateFilePath = 'file://' . app_path('cert' . DIRECTORY_SEPARATOR . 'wx') . 'wechatpay_334529128E0E9ACA0C10F04E610FFC5001C2A533.pem';
        $platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);

        // 从「微信支付平台证书」中获取「证书序列号」
        $platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);

        // 构造一个 APIv3 客户端实例
        $instance = Builder::factory([
            'mchid' => config('wechatpay.merchant_id'),
            'serial' => $merchantCertificateSerial,
            'privateKey' => $merchantPrivateKeyInstance,
            'certs' => [
                $platformCertificateSerial => $platformPublicKeyInstance,
            ],
        ]);

        return $instance;
    }

    /**
     * APP支付-APP下单(返回预支付交易会话标识)
     *
     * @param $params
     * @return mixed
     * @throws LogicException
     */
    public function v3PayTransactionsApp($params)
    {
        try {
            $instance = $this->getInstance();
            $resp = $instance
                ->chain('v3/pay/transactions/app')
                ->post(['json' => [
                    'mchid' => config('wechatpay.merchant_id'),
                    // 'out_trade_no' => md5('TEMP' . Carbon::now()->timestamp),
                    'out_trade_no' => $params['outTradeNo'],
                    'appid' => config('wechatpay.app_id'),
                    'description' => $params['subject'],
                    'attach' => $params['attach'],
                    'notify_url' => $params['notify_url'],
                    'amount' => [
                        'total' => (int)$params['totalAmount'], // 订单总金额，单位为分
                        'currency' => 'CNY' // CNY：人民币，境内商户号仅支持人民币。
                    ],
                ]]);

            // echo $resp->getStatusCode(), PHP_EOL;
            // echo $resp->getBody(), PHP_EOL;
            return json_decode($resp->getBody()->__toString(), true);
        } catch (Exception $e) {
            // 进行错误处理
            // echo $e->getMessage(), PHP_EOL;
            if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
                $r = $e->getResponse();
                // echo $r->getStatusCode() . ' ' . $r->getReasonPhrase(), PHP_EOL;
                // echo $r->getBody(), PHP_EOL, PHP_EOL, PHP_EOL;
                throw new LogicException($r->getBody());
            } else {
                throw new LogicException($e->getMessage());
            }
            // echo $e->getTraceAsString(), PHP_EOL;
        }
    }

    /**
     * APP支付-APP调起支付API(签名)
     *
     * @param $prePayId
     * @return array
     */
    public function v3AppSign($prePayId)
    {
        // 从本地文件中加载「商户API私钥」，「商户API私钥」会用来生成请求的签名
        $merchantPrivateKeyFilePath = 'file://' . app_path('cert' . DIRECTORY_SEPARATOR . 'wx') . 'apiclient_key.pem';
        $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);

        $params = [
            'appid' => config('wechatpay.app_id'),
            'timestamp' => Formatter::timestamp(),
            'noncestr' => Formatter::nonce(),
            'prepayid' => $prePayId,
        ];
        $str = Formatter::joinedByLineFeed(...array_values($params));
        $params += ['sign' => Rsa::sign(
            $str,
            $merchantPrivateKeyInstance
        )];

        $params['package'] = 'prepay_id=' . $prePayId;
        $params['partnerid'] = config('wechatpay.merchant_id');
        return $params;
    }

    /**
     * APP支付-回调通知
     *
     * @param $params
     * @return array
     */
    public function v3AppNotify($params)
    {
        // 转换通知的JSON文本消息为PHP Array数组
        // $inBodyArray = (array)json_decode($resource, true);
        // 使用PHP7的数据解构语法，从Array中解构并赋值变量
        ['resource' => [
            'ciphertext' => $ciphertext,
            'nonce' => $nonce,
            'associated_data' => $aad
        ]] = $params;
        // 加密文本消息解密
        $inBodyResource = AesGcm::decrypt($ciphertext, 'd84755322f8c84ac7fc16454ac2f5562', $nonce, $aad);
        // 把解密后的文本转换为PHP Array数组
        $inBodyResourceArray = (array)json_decode($inBodyResource, true);
        // print_r($inBodyResourceArray);// 打印解密后的结果
        return $inBodyResourceArray;
    }

    /**
     * 退款
     *
     * @param $params
     * @return mixed
     * @throws LogicException
     */
    public function v3RefundDomesticRefunds($params)
    {
        // dd($params);
        try {
            $instance = $this->getInstance();
            $resp = $instance
                ->chain('v3/refund/domestic/refunds')
                ->post(['json' => [
                    'out_trade_no' => $params['out_trade_no'],
                    'out_refund_no' => $params['out_refund_no'],
                    'notify_url' => $params['notify_url'] ?? '',
                    'amount' => [
                        'refund' => (int)$params['refund_amount'], // 退款金额，单位为分
                        'total' => (int)$params['payment_amount'],
                        'currency' => 'CNY', // CNY：人民币，境内商户号仅支持人民币。
                    ],
                ]]);
            return json_decode($resp->getBody()->__toString(), true);
        } catch (Exception $e) {
            // 进行错误处理
            if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
                $r = $e->getResponse();
                throw new LogicException($r->getBody());
            } else {
                throw new LogicException($e->getMessage());
            }
        }
    }

    /**
     * 发起商家转账API
     *
     * @param $params
     * @return mixed
     * @throws LogicException
     */
    public function v3TransferBatches($params)
    {
        try {
            $instance = $this->getInstance();
            $resp = $instance
                ->chain('v3/transfer/batches')
                ->post(['json' => [
                    // 'appid' => config('wechatpay.app_id'),
                    'appid' => '1586464848',
                    'out_batch_no' => $params['out_batch_no'],
                    'batch_name' => $params['batch_name'] ?? '批次名称',
                    'batch_remark' => $params['batch_remark'] ?? '批次备注',
                    'total_amount' => $params['total_amount'] * 100,
                    'total_num' => $params['total_num'],
                    'transfer_detail_list' => $params['transfer_detail_list'],
                ]]);
            return json_decode($resp->getBody()->__toString(), true);
        } catch (Exception $e) {
            // 进行错误处理
            if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
                $r = $e->getResponse();
                throw new LogicException($r->getBody());
            } else {
                throw new LogicException($e->getMessage());
            }
        }
    }






    // 就用一次, 下次自理
    public function getSign()
    {
        $url = 'https://api.mch.weixin.qq.com/v3/certificates';
        // Authorization: <schema> <token>
        $url_parts = parse_url($url);
        $http_method = 'GET';
        $canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
        $timestamp = Carbon::now()->timestamp;
        $nonce = '593BEC0C930BF1AFEB40B4A08C8FB242';
        $body = '';
        $message = $http_method . "\n" .
            $canonical_url . "\n" .
            $timestamp . "\n" .
            $nonce . "\n" .
            $body . "\n";

        $mch_private_key = file_get_contents(app_path('cert' . DIRECTORY_SEPARATOR . 'wx') . 'apiclient_key.pem');
        openssl_sign($message, $raw_sign, $mch_private_key, 'sha256WithRSAEncryption');
        $sign = base64_encode($raw_sign);

        $schema = 'WECHATPAY2-SHA256-RSA2048';
        $token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
            '1626405534', $nonce, $timestamp, '3DFAE1E08F8D461777A1212215DB4A91A69D2CAF', $sign);

        echo $token;
    }

    // 就用一次, 下次自理
    public function decrypt()
    {
        // 加密文本消息解密
        // 把解密后的文本转换为PHP Array数组
        $inBodyResource = AesGcm::decrypt('GQJZcmufCPlGZLePflem7RahKzTg0X45++flYhWB1njgKoom3O/ErDTnkilS3CavO8FUmhuDqvEDDbKJuQEhKG4buT3M95p/D5n8wI5s39DYw94iXZwqfE507hNTHde9qFyaz7OrE36xtkMT4ewQPhZ+k1vR/KTHpicue70eg5lzKSXu8r/IXAqdGCNxMJ/CgFwhUzPy0CO0aDeVaBotEt6q8hGUaKBDNVNvhcBY6q6G6T7odAZe/8u/8KC0plKBuJLe534+gt6fjFgtJqA+NNdUiTIaKbWric78QbvkF4wp9Ku6HZxogQXBQpwL8rs1EHCIYNHlcqRMEJ+F89fMKRd62tkcVokg1SBxUcWI/CFbkoPe+b1yyj0LTDRz4HyRkI/VYMw4u6cOC4ecE8BBLbHFslNY8qyuaVvbvHzQ+h9BBcI/r5oOZvpIxXRcDdnsjIg/A02xup0pfEvb8n7GFOeDxXgG8WgQrwowlzHagxzaE9EZT338VYPcEYZwl8wGvWj55EVtDKuC1Y/Ljc/ldxL/bFUqTb8pxEtBdc7Ex9L7T+GkXdqT6Y4srDPhv1EJItIGbHL2q9zO1hKBL5Kpxciwf0+HAXyCIzWApsMOcwCQW1p2vdNhCT4rpNrqCDJp7/RlrlMBoOsHFHnel1ZiB41fPan3W1yo7y/f4s5Xj2TCjGqHecCn7KHbuqB3S04DADL8kcn2JITSXEEJxLq7+Lq3qFZ7REPhFax4RARbwECl+9g4sT6sQTmv+E7h10X4VkKMufGlmE7+1om8c5mUr/2/ElzhuPlaat/5NcaNmx2ekhzayo/6c+1gTJzDfemUsY4jaPjRWDBpFKyorJrhgmHSiSuCTO5Rffo2x2IHLlubYk76URd2aJxZT1tzL7o+1DpjJ2GSR8aeEHoBXoD7P77FPLAd01hfJnPL9NMrIO1ysUKLmhslx6wocdVn+wCiMyjO+/ZUHZexQU00xfMUi0hmnsXmBwD5DCqx7FiGof3vwHMZQG0ndYp9C47zDc9cGMTYeFdU/BFHJxBc7m/5ljdzig1NlXiZo1WiZkh+J+GQZCHU7hHabHz9R2zUXvFLG4ILwQZ/6aDeiGeW7WXTPHO4XAjcuXQMxMkCwY6Fvm3dwrEW3/CUcy6cDN21O+cUmEWLlb/VqwZ9Yaw9ma8Ri5KgRMxXXne9Pkg0s8zM8rraChSJZbCVnAIIA2NS6SageuJrGhKfoyVAZ4J1FD41GoZ0p1HdpSo0KwM4iaWlh2tCUO7fQ+x9sHv8jjW69t3b7IivETDVC/Q+kDgQfYYJmGobsUeiYbmwuTZ4oodIfEn6/lp0IvIKQ9bRV/ree1DS9woAzYaHCbcWZLbG/H/WPavxThTXNYUhTxpRRFv9o+78WywBtaRGPprsvDZCnGo4TY560wBiHk/M1ebuliX9s7sUr9zSBRhkFeqnxxaUSodSXf1nX2LsQ6wJsU9HAdl6MsrQA0xEYNpW2GLd80Npsmagkh3qeeiXKgqlhtz1JePmgc6KC6XgFq0a0S2N4EsTEeBKu6v1+CAoVzjSvEu1pWj9PicgdQUymUtt/0cUJqoPWiSutBgx6ZFDQZs6teHYTMjYRsJR5M6C7nxycLcEh9Li50i4HQS/WVU/38qJxodQsLFl79W/bJ7T3ea5Q9MM/oM/mR+fn25z8YY/nzlqwnagrolk0gIsLBFRnRlI9AeHEEfKynuHT1q9EJ3DIyw8WKFjhZx5Vn8+TAf3C/lHczyFX1IZJRFZ6+HuDjp3mfbLk3WHnJxrGwonO/Q8+zYmOZiiHqDCP1Gy8e6swARWN/SzHtIAu62I37/oQksZ+d8G44EGi2o0DyJl0Vkpq5T9g4Puui2GK8EAgEmgPA/quCVnQAWq4Q==', 'd84755322f8c84ac7fc16454ac2f5562', '2d80ee5adc8b');
        $inBodyResourceArray = (array)json_decode($inBodyResource, true);
        dd($inBodyResourceArray);
    }
}
